#include "pinctrl.h"
#include "uart.h"
#include "osal_debug.h"
#include "soc_osal.h"
#include "app_init.h"
#include "string.h"
#include "cmsis_os2.h"
#include "EC800M.h"
#include "gpio.h"


#define DELAY_TIME_MS 1
//#define UART2_RECV_SIZE 512

uart2_recv_data uart2_recv = {0};

char ec800m_sendbuff[255] = {0};
int at_num = 0;

int temperature=24;

uint8_t latitude[20] = {0};
uint8_t longitude[20] = {0};

char ec800m_sendcmd[255] = {0};//构建 MQTT 用户名配置指令,作用是将 ATCmds[14] 的 AT 指令模板（如 AT+QMTCONN）与 MQTT_USER_NAME 动态结合，生成完整的 MQTT 连接指令
char ec800m1_sendcmd[255] = {0};//构建传感器数据与 GPS 的 JSON 上报字符串,通过 ATCmds[15] 的 JSON 模板（如 {"services":[{"properties":...}]}），将传感器数据（温度、湿度、电压）和 GPS 坐标（latitude、longitude）格式化为符合物联网平台要求的字符串。



EC800_ATCmds AT_Cmds[] = {
    {"ATE0\r\n", "OK"},
    {"AT+QGPSCFG=\"outport\",\"uartdebug\"\r\n", "OK"},//outport配置 NMEA 语句输出端口,"uartdebug" 通过调试串口输出,NMEA是GPS/北斗等卫星导航系统的标准数据格式，包含时间、经纬度、速度、卫星状态等关键信息
    {"AT+QGPSCFG=\"nmeasrc\",1\r\n", "OK"},    //设置 AT+QGPSCFG="nmeasrc",1 启用通过 AT+QGPSGNMEA 获取指定的NMEA 语句；如果设置 AT+QGPSCFG="nmeasrc",0，则不能通过 AT+QGPSGNMEA获取指定的 NMEA 语句。
    {"AT+QGPSCFG=\"gpsnmeatype\",63\r\n", "OK"},//设置输出的 NMEA 语句类型。取值为异或运算格式。范围：0~63。默认值：,输出所有类型的语句
    {"AT+QGPSCFG=\"gnssconfig\",0\r\n", "OK"},//配置支持的 GNSS 卫星导航系统,0表示GPS
    {"AT+QGPSCFG=\"autogps\",0\r\n", "OK"},//该命令用于配置模块启动时，是否自动启动 GNSS。,0表示禁用
    {"AT+QGPSCFG=\"apflash\",0\r\n", "OK"},//apflash该命令用于删除辅助数据，以便进行 GNSS 冷启动、热启动和温启动操作，且仅可在 GNSS 打开时执行,0表示删除所有辅助数据。开启 GNSS 后，强制冷启动。
    {"AT+QGPS=1\r\n", "OK"},//该命令用于打开 GNSS 或者唤醒 GNSS 功能,1 GNSS打开
    {"AT+CSQ\r\n", "OK"},//AT+CSQ 是用于查询无线通信模块（如GSM/GPRS、4G模块）的信号强度和误码率的关键指令。
    {"AT+CPIN?\r\n", "OK"},//AT+CPIN? 是用于查询SIM卡PIN码状态的核心AT指令，可帮助开发者确认SIM卡是否已解锁并准备就绪。
    {"AT+CREG?\r\n", "OK"},//AT+CREG? 是用于查询设备网络注册状态的核心AT指令，可获取设备与蜂窝网络（如GSM/4G）的连接状态及位置信息。
    {"AT+CGATT?\r\n", "OK"},//AT+CGATT? 是用于查询移动终端GPRS附着状态的核心AT指令，可判断设备是否成功连接到蜂窝网络的数据服务
    {"AT+QMTOPEN=0,\"%s\",1883\r\n", "OK"},//AT+QMTOPEN=0,"%s",1883 是用于建立与MQTT服务器的TCP连接的核心指令
    {"AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"\r\n", "OK"},//建立 MQTT 会话：与服务器建立协议层连接，支持客户端认证。​参数配置：通过客户端标识符（Client ID）、用户名（Username）、密码（Password）进行身份验证。
    {"AT+QMTPUB=0,0,0,0,\"$oc/devices/%s/sys/properties/report\"\r\n", ">"}, // 14,    AT+QMTPUB=0,0,0,0,"$oc/devices/%s/sys/properties/report" 是向华为云上报设备属性的核心命令
    /*{"{\"services\":[{\"service_id\":\"attribute\",\"properties\":{\"temperature\":%d,\"humidity\":%d,"
     "\"voltage\":%.1f,\"car\":\"%s\",\"Latitude\":\"%s\",\"Longitude\":\"%s\"}}]}",
     "OK"},//华为云物联网设备属性上报JSON数据*/

     {"{\"services\":[{\"service_id\":\"attribute\",\"properties\":{\"temperature\":%d,\"Latitude\":\"%s\",\"Longitude\":\"%s\"}}]}",
     "OK"},//华为云物联网设备属性上报JSON数据

    /* {"{\"services\":[{\"service_id\":\"attribute\",\"properties\":{\"temperature\":%d}}]}",
     "OK"},*/   //华为云物联网设备属性上报JSON数据,现在只有温度测试数据


    {"\x1A\0", "OK"},//指令作用：0x1A（Ctrl+Z）是串口通信中标记数据流结束的标准字符，用于通知模块已发送完当前数据包。​典型场景：在MQTT数据发布（如AT+QMTPUB）或短信发送后，发送0x1A触发模块立即处理数据。
    {"AT+QGPSLOC=0\r\n", "OK"},//实时位置查询
    {"AT+QMTDISC=0\r\n", "OK"},//连接资源释放


    




};

EC800_ATCmds AT_Call_Phone[] = {
    {"AT13189941518;\r","OK"}//电话功能

};

void ec800_pwr_init(void)
{

    uapi_pin_set_mode(GPIO_10, HAL_PIO_FUNC_GPIO);// 配置为GPIO功能
    uapi_gpio_set_dir(GPIO_10, GPIO_DIRECTION_OUTPUT);// 设置为输出模式
    uapi_gpio_set_val(GPIO_10, GPIO_LEVEL_LOW);

    uapi_gpio_set_val(GPIO_10, GPIO_LEVEL_HIGH);// 拉高电平启动模块
    osal_mdelay(3000); // 维持3秒
    uapi_gpio_set_val(GPIO_10, GPIO_LEVEL_LOW);// 恢复低电平

}

void uart2_init_config(void)
{
    uart_buffer_config_t g_app_uart2_buffer_config = {.rx_buffer = uart2_recv.recv, .rx_buffer_size = UART2_RECV_SIZE};
    uapi_pin_set_mode(GPIO_07, PIN_MODE_2);
    uapi_pin_set_mode(GPIO_08, PIN_MODE_2);
    uart_attr_t attr = {
        .baud_rate = 115200, .data_bits = UART_DATA_BIT_8, .stop_bits = UART_STOP_BIT_1, .parity = UART_PARITY_NONE};

    uart_pin_config_t pin_config = {.tx_pin = GPIO_08, .rx_pin = GPIO_07, .cts_pin = PIN_NONE, .rts_pin = PIN_NONE};
    uapi_uart_deinit(UART_BUS_2);
    int ret = uapi_uart_init(UART_BUS_2, &pin_config, &attr, NULL, &g_app_uart2_buffer_config);
    if (ret != 0) {
        printf("uart init failed ret = %02x\n", ret);
    }


    
}

void uart2_read_handler(const void *buffer, uint16_t length, bool error)
{
    unused(error);//unused(error); 是用于抑制编译器未使用变量警告的常见操作.当函数的参数（如error）未被使用时，编译器会触发 -Wunused-parameter 警告。通过 unused(error); 显式标记该参数为“已处理但未使用”，消除警告
    if (buffer == NULL || length == 0) {
        return;
    }
    if (memcpy_s(uart2_recv.recv, length, buffer, length) != EOK) {
        return;
    }
    
    
}


int ec800_Call_Phone(void)
{
    int call_phone_state;
    uart_send_buff((uint8_t *)AT_Call_Phone[0].ATSendStr, strlen(AT_Call_Phone[0].ATSendStr));
    osal_msleep(5000);
    if (strstr((char *)uart2_recv.recv, AT_Call_Phone[0].ATRecStr) != NULL) {//通过字符串匹配判断是否包含预期响应（如OK\r\n）。匹配成功则递增at_num，进入下一条AT指令发送流程；失败则重试当前指令。
                printf(" %s call phone succeed !!!\n", AT_Call_Phone[0].ATSendStr);
                call_phone_state=0;
                
            } else {
                printf(" %s call phone fail !!!\n", AT_Call_Phone[0].ATSendStr);
                call_phone_state=1;  
            }
            memset(uart2_recv.recv, 0, sizeof(uart2_recv.recv));
            return call_phone_state;
}


void uart2_init(void)
{
     uart2_init_config();
    uapi_uart_register_rx_callback(2, UART_RX_CONDITION_MASK_IDLE, 1, uart2_read_handler);
    //ec800_pwr_init();
    
}

void EC800M_init(void)
{
    
    uart2_init_config();
    uapi_uart_register_rx_callback(2, UART_RX_CONDITION_MASK_IDLE, 1, uart2_read_handler);

  
   while (at_num < 14) {

        if (at_num == 12) {                                     //AT+QMTOPEN=0,"%s",1883 是用于建立与MQTT服务器的TCP连接的核心指令
            memset(ec800m_sendbuff, 0, sizeof(ec800m_sendbuff));//这行代码用于将 ec800m_sendbuff 数组的所有字节初始化为 0，实现内存块的快速清空或初始化。
            if (sprintf(ec800m_sendbuff, AT_Cmds[12].ATSendStr, HUWWEIYUN_MQTT_IP) > 0)//代码通过sprintf函数将MQTT服务器IP地址动态嵌入AT指令模板，实现指令的动态组装。这种格式化的核心在于%s占位符被替换为实际IP地址，双引号通过转义字符\"
                uart_send_buff((uint8_t *)ec800m_sendbuff, strlen(ec800m_sendbuff));
                
        } else if (at_num == 13) {
            memset(ec800m_sendbuff, 0, sizeof(ec800m_sendbuff));//建立 MQTT 会话：与服务器建立协议层连接，支持客户端认证。​参数配置：通过客户端标识符（Client ID）、用户名（Username）、密码（Password）进行身份验证。
            if (sprintf(ec800m_sendbuff, AT_Cmds[13].ATSendStr, MQTT_CLIENT_ID, MQTT_USER_NAME, MQTT_PASS_WORD) > 0)
                uart_send_buff((uint8_t *)ec800m_sendbuff, strlen(ec800m_sendbuff));
                
        } else {
            memset(ec800m_sendbuff, 0, sizeof(ec800m_sendbuff));
            if (sprintf(ec800m_sendbuff, AT_Cmds[at_num].ATSendStr, MQTT_CLIENT_ID, MQTT_USER_NAME, MQTT_PASS_WORD) > 0)
            {
                uart_send_buff((uint8_t *)AT_Cmds[at_num].ATSendStr, strlen(AT_Cmds[at_num].ATSendStr));
                
            }
            
                
        }

        memset(uart2_recv.recv, 0, sizeof(uart2_recv.recv));
        if (uapi_uart_read(UART_BUS_2, uart2_recv.recv, UART2_RECV_SIZE, 0)) {
            printf("[AT RECV %s]\n", uart2_recv.recv);
            if (strstr((char *)uart2_recv.recv, AT_Cmds[at_num].ATRecStr) != NULL) {//通过字符串匹配判断是否包含预期响应（如OK\r\n）。匹配成功则递增at_num，进入下一条AT指令发送流程；失败则重试当前指令。
                printf(" %s succeed !!!\n", AT_Cmds[at_num].ATSendStr);
                at_num++;
                
                
            } else {
                printf(" %s fail !!!\n", AT_Cmds[at_num].ATSendStr);
                
            }
            memset(uart2_recv.recv, 0, sizeof(uart2_recv.recv));
        }
        
    }
    
    
    
}




/*从串口2获取AT指令返回的定位等信息*/
void uart2_recv_Data(void)
{
    
    uint8_t len = 0;
    uint16_t times = 0;
    while (1) {

        if (uart2_recv.recv_flag) {
            if (times % 100 == 0) {//每隔一段时间去获取GPS数据
                printf("GPS_Data\n");
                uart_send_buff((uint8_t *)AT_Cmds[17].ATSendStr, strlen(AT_Cmds[17].ATSendStr));
            }
            len = uapi_uart_read(UART_BUS_2, uart2_recv.recv, 512, 0);
            if (len > 0) {
                printf("GPS uart2_recv.recv %d: %s\n", len, uart2_recv.recv);
                get_gps_coordinates((char *)uart2_recv.recv);//解析收到的GPS数据
                memset(uart2_recv.recv, 0, sizeof(uart2_recv.recv));
            }
        }
        osal_msleep(100);
        times++;
    }
}




void uart2_send_Data(void )
{
    ec800_pwr_init();
    EC800M_init();
    
    
     
   
    uart2_recv.recv_flag = 0;
    sprintf(ec800m_sendcmd, AT_Cmds[14].ATSendStr, MQTT_USER_NAME);//该代码用于构建EC800M模组的MQTT用户名配置指令：
    while (1) {
        sprintf(ec800m1_sendcmd, AT_Cmds[15].ATSendStr, (int)temperature,latitude,
                longitude);//将double型GPS坐标等异构数据，按ATCmds[15].ATSendStr定义的格式统一转换为字符串形式

        /*sprintf(ec800m1_sendcmd, AT_Cmds[15].ATSendStr, (int)temperature
                );*///将double型GPS坐标等异构数据，按ATCmds[15].ATSendStr定义的格式统一转换为字符串形式
        uart_send_buff((uint8_t *)ec800m_sendcmd, strlen(ec800m_sendcmd));
        osal_msleep(100);
        uart_send_buff((uint8_t *)ec800m1_sendcmd, strlen(ec800m1_sendcmd));
        osal_msleep(100);
        uart_send_buff((uint8_t *)AT_Cmds[16].ATSendStr, strlen(AT_Cmds[16].ATSendStr));
        osal_msleep(2000);
    }
}


/* 函数用于解析 EC800M 4G 模块返回的 GPS 定位数据*/
void get_gps_coordinates(const char *input)
{
    const char *locLine = strstr(input, "+QGPSLOC:");
    if (locLine == NULL) {
        return;
    }
    locLine += strlen("+QGPSLOC:"); // 跳过 "+QGPSLOC:"

    // 跳过第一个逗号分隔的部分（时间信息）
    while (*locLine != ',' && *locLine != '\0') {
        locLine++;
    }
    locLine++; // 跳过逗号

    // 提取纬度信息
    uint8_t i = 0;
    while ((*locLine != ',') && (i < sizeof(latitude) - 2)) {
        latitude[i++] = *locLine++;
    }
    latitude[i] = '\0';
    locLine++; // 跳过逗号

    // 提取经度信息
    i = 0;
    while ((*locLine != ',') && (i < sizeof(longitude) - 2)) {
        longitude[i++] = *locLine++;
    }
    longitude[i] = '\0';
}


// 发送数据包
uint32_t uart_send_buff(uint8_t *str, uint16_t len)
{
    uint32_t ret = 0;
    // printf("[AT SEND ]: %s\n", str);
    ret = uapi_uart_write(UART_BUS_2, str, len, 0xffffffff);
    if (ret != 0) {
        return ret;
    }
    return ret;
}